-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

require 'mdb.bmc.kepler.Systems.Mctp.MctpCtrl'
local mctp_object = require 'mctp.mctp_object'
local log = require 'mc.logging'
local singleton = require 'mc.singleton'
local lib_sml = require 'sml.core'
local sml = require 'sml'
local c_tasks = require 'tasks'
local class = require 'mc.class'
local mctp_lib = require 'mctp_lib'
local storage_bus = require 'storage_bus'
local cm = require 'common_def'
local mctp_infos = lib_sml.mctp_infos

local MCTP_UPDATE_INTERVAL <const> = 5000
local TASK_MCTP <const> = 'mctp_start'

local mctp_service = class()

function mctp_service:ctor()
    self.m_mctp_collection = {}
    self.bus = storage_bus.get_instance().bus
    self.mctp_start_task = nil
    self.mctp_state = false
    self.os_state = false
end

function mctp_service:init()
    self.mctp_start_task = c_tasks.get_instance():new_task(TASK_MCTP):loop(function(task)
        self:prepare()
        if self.mctp_state then
            task:stop()
            self.mctp_start_task = nil
        end
    end):set_timeout_ms(MCTP_UPDATE_INTERVAL)
end

function mctp_service:change_power_state(status)
    self.os_state = status
end

function mctp_service:set_pmc_eid(controller, phy_addr)
    if not mctp_object.is_pmc_raid(controller.TypeId) then
        return
    end

    local ok, ep = pcall(mctp_lib.get_pcie_endpoint, self.bus, phy_addr, mctp_lib.MCTP_MESSAGE_TYPE_VDPCI)
    if not ok or not ep then
        log:error('[storage] get mctp endpoint failed, Id = %s, Endpoint = %s', controller.Id, ep)
        return
    end
    controller.Eid = ep.TargetEid
end

-- 向驱动注册消息类型与设备的绑定关系
function mctp_service:register_mctp_object(controller)
    local object = mctp_object.new(self.bus, controller)
    object:register_ctrl_mctp_transport()
    self.m_mctp_collection[object.id] = object
    mctp_service:set_pmc_eid(controller, object.phy_addr)
    mctp_infos.add_mctp_info(object.id, object.eid, object.phy_addr, object.vendor_id)
end

---@param controller c_controller
function mctp_service:unregister_mctp_object(controller)
    if not self.m_mctp_collection[controller.Id] then
        return
    end

    self.m_mctp_collection[controller.Id] = nil
    mctp_infos.del_mctp_info(controller.Id)
end

function mctp_service:prepare()
    if not self.os_state then
        return
    end
    local obj = mctp_lib.get_mctp_pcie_binding(self.bus)
    if not obj then
        log:debug('[Storage] Get BMC Mctp info failed')
        return
    end
    -- 将BMC的mctp信息传递给C库的线程
    sml.mctp_infos.set_bmc_mctp_info(obj.BmcEid, obj.BmcPhyAddr)
    self.mctp_state = true
    log:notice('[Storage] mctp prepare finished. bmc_eid = %s bmc_phy = %s state = %s',
        obj.BmcEid, obj.BmcPhyAddr, self.mctp_state)
end

return singleton(mctp_service)
